package com.lambkit.core.limiter.impl;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import com.lambkit.core.cache.ICache;
import com.lambkit.core.limiter.Limiter;

/**
 * 信号量控制算法<br/>
 * 用来控制同时访问特定资源的线程数量，通过协调各个线程，以保证合理的使用资源。<br/>
 * 通常用于那些资源有明确访问数量限制的场景，常用于限流。<br/>
 * 比如：<br/>
 * 数据库连接池，同时进行连接的线程有数量限制，连接不能超过一定的数量，
 * 当连接达到了限制数量后，后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。<br/>
 * @author yangyong
 *
 */
public class SemaphoreLimiter implements Limiter {

	private static final String SEMAPHORE_LIMITER_CACHE_NAME = "lambkit_limiter_semaphore";
	
	private ICache iCache;

	public SemaphoreLimiter(ICache iCache) {
		this.iCache = iCache;
	}
	
	public Semaphore getSemaphore(String resource, int rate) {
		Semaphore semaphore = iCache.get(SEMAPHORE_LIMITER_CACHE_NAME, resource);
        if (semaphore==null) {
        	semaphore = new Semaphore(rate);
            iCache.put(SEMAPHORE_LIMITER_CACHE_NAME, resource, semaphore);
        }
        return semaphore;
	}
	
	/**
     * 限制时长默认为1秒
     */
    public boolean tryAcquire(String resource, int rate) {
        return tryAcquire(resource, rate, DEFAULT_PERIOD);
    }
    
    /**
     * 尝试是否能正常执行
     *
     * @param resource      资源名
     * @param rate          限制次数
     * @param periodSeconds 限制时长，单位为秒
     * @return true 可以执行
     * false 限次，禁止
     */
    public boolean tryAcquire(String resource, int rate, int periodSeconds) {
    	return tryAcquire(resource, rate, periodSeconds, DEFAULT_TIME_UNIT);
    }
	
	@Override
	public boolean tryAcquire(String resource, int rate, int period, TimeUnit timeUnit) {
		
		Semaphore semaphore = getSemaphore(resource, rate);
		return semaphore.tryAcquire();
	}

	@Override
	public void release(String resource) {
		Semaphore semaphore = iCache.get(SEMAPHORE_LIMITER_CACHE_NAME, resource);
		if(semaphore!=null) {
			semaphore.release();
		}
	}

}
